package com.hd.sup.config.mybatis.interceptor;

import cn.hutool.core.util.ReflectUtil;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.hd.common.constant.BizConstant;
import com.hd.common.exception.SupException;
import com.hd.common.utils.SpringContextUtils;
import com.hd.common.utils.UserUtils;
import com.hd.sup.config.mybatis.DataScopeUtils;
import com.hd.sup.core.annotation.DataScope;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import java.sql.Connection;
import java.util.List;
import java.util.Properties;
import java.util.stream.Collectors;

/**
 * @version V1.0
 * @Project 交通运输第三方监测平台
 * @Title MybatisSpringPageInterceptor.java
 * @Description Mybatis 分页拦截器
 * 原生sql提取操作一定要放在分页拦截器前面
 * @Package com.hd.sup.config.mybatis.interceptor
 * @date 2021/1/25 9:08 上午
 * @Copyright: 深圳市宏电技术股份有限公司 All rights reserved.
 */
@Slf4j
@Intercepts({@Signature(method = "prepare", type = StatementHandler.class, args = {Connection.class, Integer.class}),
        @Signature(method = "query", type = Executor.class, args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})
public class DataScopeInterceptor implements Interceptor {


    public static final String MYSQL = "mysql";
    public static final String ORACLE = "oracle";

    /**
     * @Description 数据库类型
     * @Create: 2021/1/25 9:09 上午
     */
    protected String databaseType;

    /**
     * @Description 本地分页线程对象
     * @Create: 2021/1/25 9:10 上午
     */
    protected ThreadLocal<Page> pageThreadLocal = new ThreadLocal<>();

    public String getDatabaseType() {
        return databaseType;
    }

    public void setDatabaseType(String databaseType) {
        if (!databaseType.equalsIgnoreCase(MYSQL) && !databaseType.equalsIgnoreCase(ORACLE)) {
            throw new SupException("Page not support for the type of database, database type [" + databaseType + "]");
        }
        this.databaseType = databaseType;
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
        String databaseType = properties.getProperty("databaseType");
        if (databaseType != null) {
            setDatabaseType(databaseType);
        }
    }

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        UserUtils userUtils = SpringContextUtils.getBean(UserUtils.class);
        boolean isAdmin;
        try {
            isAdmin = userUtils.isAdmin();
        } catch (Exception e) {
            return invocation.proceed();
        }
        //admin 无需权限拦截
        if (isAdmin) return invocation.proceed();

        // 控制SQL和查询总数的地方
        if (invocation.getTarget() instanceof StatementHandler) {
            StatementHandler statementHandler = PluginUtils.realTarget(invocation.getTarget());
            MetaObject metaStatementHandler = SystemMetaObject.forObject(statementHandler);
            MappedStatement mappedStatement = (MappedStatement) metaStatementHandler.getValue("delegate.mappedStatement");

            BoundSql boundSql = statementHandler.getBoundSql();
            String sql = boundSql.getSql();

            //只有查询区分权限
            if (!SqlCommandType.SELECT.equals(mappedStatement.getSqlCommandType())) return invocation.proceed();


            //从注解中读取
            DataScope dataScope = DataScopeUtils.getDataScopeByDelegate(mappedStatement);

            //注解存在 则应用数据权限
            if (dataScope != null) {
                //返回拦截包装后的sql
                sql = permissionSql(sql);
                ReflectUtil.setFieldValue(boundSql, "sql", sql);
            }

        }
        return invocation.proceed();
    }

    /**
     * @return java.lang.String
     * @description 数据权限sql包装
     * @date 2021/1/25 9:15 上午
     * @params [sql]
     */
    protected String permissionSql(String sql) {
        StringBuilder sbSql = new StringBuilder(sql);
        UserUtils userUtils = SpringContextUtils.getBean(UserUtils.class);
        List<String> deptIds = userUtils.getDeptIdList();
        if (CollectionUtils.isNotEmpty(deptIds)) {
            sbSql = new StringBuilder("SELECT * FROM (")
                    .append(sbSql)
                    .append(" ) temp ")
                    .append(" WHERE temp.dept_id IN (")
                    .append(deptIds.stream().map(one -> "'" + one + "'").collect(Collectors.joining(BizConstant.Common.SEPARATOR.key()))).append(")");
        }
        return sbSql.toString();
    }
}
